/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.dialog.ac.view;

import java.util.logging.Level;
import java.util.logging.Logger;
import java.awt.Color;
import java.awt.Component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JTable;
import javax.swing.ListCellRenderer;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import offset.nodes.Constants;
import offset.nodes.client.chooser.view.QueryResultChooserDialog;
import offset.nodes.client.view.SimpleDialog;
import offset.nodes.client.dialog.ac.model.AccessControlModel;

/**
 *
 * @author  Walter Lütgenau
 */
public class AccessControlPanel extends javax.swing.JPanel {

    static class PrincipalListModel extends DefaultListModel {

        public void addUser(User user) {
            add(getSize(), user);
        }

        public void addRole(Role role) {
            add(0, role);
        }
        
        public void addPrincipal(Principal principal) {
            if ( principal instanceof User)
                addUser((User)principal);
            else if ( principal instanceof Role)
                addRole((Role)principal);
        }
        
        public void addAll(Collection<Principal> principals) {
            Iterator<Principal> i = principals.iterator();
            while( i.hasNext())
                addPrincipal(i.next());
        }
    }

    static class PrincipalCellRenderer extends JLabel implements ListCellRenderer {

        private static final Color HIGHLIGHT_COLOR = new Color(0, 0, 128);

        public PrincipalCellRenderer() {
            setOpaque(true);
            setIconTextGap(12);
            setBorder(new EmptyBorder(1, 5, 0, 0));
        }

        public Component getListCellRendererComponent(JList list, Object value,
                int index, boolean isSelected, boolean cellHasFocus) {
            Principal entry = (Principal) value;

            setText(entry.getName());
            setIcon(entry.getImage());

            if (isSelected) {
                setBackground(HIGHLIGHT_COLOR);
                if ( entry.isInherited())
                    setForeground(Color.gray);
                else
                    setForeground(Color.white);
            } else {
                setBackground(Color.white);
                if ( entry.isInherited())
                    setForeground(Color.gray);
                else
                    setForeground(Color.black);
            }
            return this;
        }
    }

    static class PrivilegeCellRenderer extends JLabel implements TableCellRenderer {

        private static final Color HIGHLIGHT_COLOR = new Color(0, 0, 128);

        public PrivilegeCellRenderer() {
            setOpaque(true);
            setBorder(new EmptyBorder(0, 5, 0, 0));
            setBackground(Color.white);
        }

        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            setText((String) value);
            return this;
        }
    }

    static class PrivilegesTableModel extends AbstractTableModel {

        public static final int MAX_CHECKBOX = 50;
        LinkedList<Privilege> privileges = new LinkedList<Privilege>();
        String[] columnKeys = new String[]{"privileges.columnName", "privileges.columnGranted", "privileges.columnDenied"};
        ResourceBundle bundle = ResourceBundle.getBundle("offset/nodes/client/dialog/view/DialogBundle");

        public PrivilegesTableModel() {
            privileges = new LinkedList<Privilege>();
        }

        public void addPrivilege(Privilege privilege) {
            privileges.add(privilege);
            fireTableDataChanged();
        }

        public List<Privilege> getPrivileges() {
            return privileges;
        }

        public void clear() {
            privileges.clear();
        }

        public int getRowCount() {
            return privileges.size();
        }

        @Override
        public int getColumnCount() {
            return 3;
        }

        @Override
        public String getColumnName(int column) {
            return bundle.getString(columnKeys[column]);
        }
        Class types[] = new Class[]{String.class, Boolean.class, Boolean.class};

        @Override
        public Class<?> getColumnClass(int column) {
            if (column < 0 || column >= getColumnCount())
                return null;
            return types[column];
        }

        @Override
        public Object getValueAt(int row, int column) {
            if (row < 0 || row >= privileges.size() || column < 0 || column >= getColumnCount())
                return null;

            Privilege privilege = privileges.get(row);
            return privilege.toArray()[column];
        }

        @Override
        public void setValueAt(Object value, int row, int column) {
            if (row < 0 || row >= privileges.size() || column < 0 || column >= getColumnCount())
                return;

            Privilege privilege = privileges.get(row);
            switch (column) {
                case Privilege.COLUMN_NAME:
                    //privilege.setName((String) value);
                    break;

                case Privilege.COLUMN_GRANTED:
                    if (((Boolean) value).booleanValue()) {
                        privilege.setGranted(true);
                        privilege.setDenied(false);
                        fireTableCellUpdated(row, Privilege.COLUMN_DENIED);
                    }
                    else
                        privilege.setGranted(false);
                    break;

                case Privilege.COLUMN_DENIED:
                    if (((Boolean) value).booleanValue()) {
                        privilege.setDenied(true);
                        privilege.setGranted(false);
                        fireTableCellUpdated(row, Privilege.COLUMN_GRANTED);
                   }
                    else
                        privilege.setDenied(false);
                     break;
            }
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            if (columnIndex == Privilege.COLUMN_GRANTED || columnIndex == Privilege.COLUMN_DENIED)
                return true;
            return false;
        }
    }
    
    AccessControlModel model = null;
    String path = null;

    PrivilegesTableModel privilegesModel = new PrivilegesTableModel();
    PrincipalListModel principalModel = new PrincipalListModel();
    Principal selectedPrincipal = null;

    /** Creates new AccessControlPanel
     * 
     * @param model 
     */
    public AccessControlPanel(AccessControlModel model, String path) {
        this.model = model;
        this.path = path;

        initComponents();
        privilegesTable.getColumnModel().getColumn(Privilege.COLUMN_DENIED).setMaxWidth(PrivilegesTableModel.MAX_CHECKBOX);
        privilegesTable.getColumnModel().getColumn(Privilege.COLUMN_GRANTED).setMaxWidth(PrivilegesTableModel.MAX_CHECKBOX);
        privilegesTable.getColumnModel().getColumn(Privilege.COLUMN_NAME).setCellRenderer(new PrivilegeCellRenderer());

        principalList.addListSelectionListener(new ListSelectionListener() {

            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting())
                    return;
                if (selectedPrincipal != null) {
                    selectedPrincipal.getPrivileges().clear();
                    Iterator<Privilege> i = privilegesModel.getPrivileges().iterator();
                    while (i.hasNext()) {
                        selectedPrincipal.getPrivileges().add(i.next());
                        i.remove();
                    }
                }
                
                selectedPrincipal = (Principal) principalList.getSelectedValue();
                if (selectedPrincipal != null) {
                    privilegesTable.setEnabled(!selectedPrincipal.isInherited());
                    privilegesModel.clear();
                    Iterator<Privilege> i = selectedPrincipal.getPrivileges().iterator();
                    while (i.hasNext()) {
                        privilegesModel.addPrivilege(i.next());
                    }
                }
            }
        });
    }

    protected void init() {
        try {
            principalModel.addAll(model.getNodeAccessControlNode(path));
            principalModel.addAll(model.getInheritedAccessControlNode(path));
            principalList.setSelectedIndex(0);            
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

    public Collection<Principal> getPrincipals() {
        ArrayList<Principal> principals = new ArrayList<Principal>();
        for( int i=0; i<principalModel.size();i++ )
            principals.add((Principal)principalModel.get(i));
        
        return principals;
    }
    
    public void setPrincipals(Collection<Principal> principals) {
        Iterator<Principal> i = principals.iterator();
        while ( i.hasNext()) {
            Principal principal = i.next();
            if ( principal instanceof Role )
                principalModel.addRole((Role) principal);
            else
                principalModel.addUser((User) principal);
        }
    }

    String[] standardPermissions = new String[]{Constants.PRIVILEGE_READ, Constants.PRIVILEGE_ADD_NODE,
        Constants.PRIVILEGE_REMOVE_NODE, Constants.PRIVILEGE_SET_PROPERTY, Constants.PRIVILEGE_REMOVE_PROPERTY};

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        principalList = new javax.swing.JList();
        jScrollPane2 = new javax.swing.JScrollPane();
        privilegesTable = new javax.swing.JTable();
        jLabel2 = new javax.swing.JLabel();
        addRoleButton = new javax.swing.JButton();
        removeButton = new javax.swing.JButton();
        addUserButton = new javax.swing.JButton();

        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("offset/nodes/client/dialog/view/DialogBundle"); // NOI18N
        jLabel1.setText(bundle.getString("privileges.userAndGroup")); // NOI18N

        principalList.setModel(principalModel);
        principalList.setCellRenderer(new PrincipalCellRenderer());
        principalList.setPreferredSize(new java.awt.Dimension(350, 95));
        jScrollPane1.setViewportView(principalList);

        jScrollPane2.setBackground(java.awt.Color.white);

        privilegesTable.setModel(privilegesModel);
        privilegesTable.setGridColor(java.awt.Color.white);
        privilegesTable.setPreferredSize(new java.awt.Dimension(350, 95));
        privilegesTable.setRowSelectionAllowed(false);
        jScrollPane2.setViewportView(privilegesTable);
        privilegesTable.getColumnModel().getColumn(Privilege.COLUMN_DENIED).setMaxWidth(PrivilegesTableModel.MAX_CHECKBOX);
        privilegesTable.getColumnModel().getColumn(Privilege.COLUMN_GRANTED).setMaxWidth(PrivilegesTableModel.MAX_CHECKBOX);

        jLabel2.setText(bundle.getString("privileges.selectedPrivileges")); // NOI18N

        addRoleButton.setText(bundle.getString("privileges.addRole")); // NOI18N
        addRoleButton.setPreferredSize(new java.awt.Dimension(77, 23));
        addRoleButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addRole(evt);
            }
        });

        removeButton.setText(bundle.getString("privileges.remove")); // NOI18N
        removeButton.setPreferredSize(new java.awt.Dimension(77, 23));
        removeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removePrincipal(evt);
            }
        });

        addUserButton.setText(bundle.getString("privileges.addUser")); // NOI18N
        addUserButton.setPreferredSize(new java.awt.Dimension(73, 23));
        addUserButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addUser(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(25, 25, 25)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 387, Short.MAX_VALUE)
                        .addContainerGap())
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, 387, Short.MAX_VALUE)
                        .addContainerGap())
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(addUserButton, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(addRoleButton, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(removeButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 356, Short.MAX_VALUE))
                        .addGap(41, 41, 41))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 356, Short.MAX_VALUE)
                        .addGap(41, 41, 41))))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(19, 19, 19)
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(removeButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(addRoleButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(addUserButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(18, 18, 18)
                .addComponent(jLabel2)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
                .addGap(50, 50, 50))
        );
    }// </editor-fold>//GEN-END:initComponents
    private void addUser(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addUser
        try {
            QueryResultChooserDialog chooser = new QueryResultChooserDialog(new JFrame(), true, model.getServer(), "//element(*," + Constants.TYPENAME_USER + ")");
            chooser.setVisible(true);
            if (chooser.getResult() == SimpleDialog.OK) {
                String name = chooser.getPath();
                if (name.indexOf("/") >= 0)
                    name = name.substring(name.lastIndexOf("/") + 1);
                User user = new User(name, chooser.getUuid());
                principalModel.addUser(user);
                principalList.setSelectedIndex(principalModel.size()-1);
                privilegesModel.clear();
                for (int i = 0; i < standardPermissions.length; i++) {
                    Privilege privilege = new Privilege(standardPermissions[i], false, false);
                    privilegesModel.addPrivilege(privilege);
                    user.getPrivileges().add(privilege);
                }
            }
        } catch (Exception ex) {
            Logger.getLogger(AccessControlPanel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_addUser

    private void addRole(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addRole
        try {
            QueryResultChooserDialog chooser = new QueryResultChooserDialog(new JFrame(), true, model.getServer(), "//element(*," + Constants.TYPENAME_ROLE + ")");
            chooser.setVisible(true);
            if (chooser.getResult() == SimpleDialog.OK) {
                String name = chooser.getPath();
                if (name.indexOf("/") >= 0)
                    name = name.substring(name.lastIndexOf("/") + 1);
                Role role = new Role(name, chooser.getUuid());
                principalModel.addRole(role);
                principalList.setSelectedIndex(0);
                privilegesModel.clear();
                for (int i = 0; i < standardPermissions.length; i++) {
                    Privilege privilege = new Privilege(standardPermissions[i], false, false);
                    privilegesModel.addPrivilege(privilege);
                    role.getPrivileges().add(privilege);
                }
            }
        } catch (Exception ex) {
            Logger.getLogger(AccessControlPanel.class.getName()).log(Level.SEVERE, null, ex);
        }

    }//GEN-LAST:event_addRole

    private void removePrincipal(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removePrincipal
        selectedPrincipal = null;
        if (principalModel.size() > 0) {
            int selected = principalList.getSelectedIndex();
            principalModel.remove(selected);
            if ( principalModel.size() > 0) {
                if ( selected > 0)
                    principalList.setSelectedIndex(selected-1);
                else
                    principalList.setSelectedIndex(0);
            }
            
        }
       
    }//GEN-LAST:event_removePrincipal

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton addRoleButton;
    private javax.swing.JButton addUserButton;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JList principalList;
    private javax.swing.JTable privilegesTable;
    private javax.swing.JButton removeButton;
    // End of variables declaration//GEN-END:variables
}
